//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.Activities
{
    #region Using Directives

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.ComponentModel;
    using System.Workflow.ComponentModel;
    using System.Collections;
    using System.Collections.Specialized;
    using System.Workflow.ComponentModel.Design;
    using System.ComponentModel.Design;
    using Microsoft.Research.ScientificWorkflow.NetCDF.CSharpAPI;
    using System.Workflow.ComponentModel.Compiler;

    using Microsoft.Research.ScientificWorkflow;
    using Microsoft.Research.ScientificWorkflow.Helpers;
    
    using WorkflowData;
    #endregion

    #region MedianTransformerActivity Class

    [Name("Variable Median Transform")]
    [Description("Transform for finding the median values for a variable")]
    [WorkflowCategory("NetCdf")]
    public class MedianTransformerActivity : Activity
    {
        #region Private Fields

        private const string HyperCuberPropertyName = "GenerateHyperCubeActivity";
        private const string MissingValue = "missing_value";

        #endregion

        #region Dependency Properties

        public static DependencyProperty VariableNameProperty = DependencyProperty.Register("VariableName",
            typeof(string), typeof(MedianTransformerActivity));

        [RequiredInputParam]
        [Name("Variable Name")]
        [Description("Variable Name for which the min/max value is to be found.")]
        public string VariableName
        {
            get
            {
                return ((string)(base.GetValue(MedianTransformerActivity.VariableNameProperty)));
            }
            set
            {
                base.SetValue(MedianTransformerActivity.VariableNameProperty, value);
            }
        }


        public static DependencyProperty MedianProperty = DependencyProperty.Register("Median",
            typeof(object), typeof(MedianTransformerActivity));

        /// <summary>
        /// Mean Output property
        /// </summary>
        [OutputParam]
        [Name("Mean Value")]
        [Description("Lowest value for the specified variable.")]
        public object Median
        {
            get
            {
                return ((object)(base.GetValue(MedianTransformerActivity.MedianProperty)));
            }
            set
            {
                base.SetValue(MedianTransformerActivity.MedianProperty, value);
            }
        }

        public static DependencyProperty InputCubeProperty =
            DependencyProperty.Register("InputCube", typeof(HyperCube), typeof(MedianTransformerActivity));

        [RequiredInputParam]
        [Name("In Hyper Cube")]
        [Description("Hyper Cube for finding Mean")]
        public HyperCube InputCube
        {
            get
            {
                return ((HyperCube)(base.GetValue(MedianTransformerActivity.InputCubeProperty)));
            }
            set
            {
                base.SetValue(MedianTransformerActivity.InputCubeProperty, value);
            }
        }
        
        #endregion

        #region Overridden Methods

        /// <summary>
        /// Extract mean value
        /// </summary>
        /// <param name="executionContext"></param>
        /// <returns></returns>
        protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
        {
            //Get the HyperCube form earlier activity and proceed for min and max
            HyperCube InputHyperCube = this.InputCube;

            int i = InputHyperCube.Schema.LookupColumnIndex(this.VariableName);
            if (-1 == i)
                throw new ArgumentException("MinMaxTransform:Invalid Variable Name");

            object missingValue = InputHyperCube.Schema.GetAttribute(i, ScalerTransform.MissingValue);;
            if (missingValue != null)
            {
                if(missingValue is System.Byte[])
                    missingValue = ((System.Byte[])missingValue)[0];
                else if (missingValue is System.Int16[])
                    missingValue = ((System.Int16[])missingValue)[0];
                else if (missingValue is System.Int32[])
                    missingValue = ((System.Int32[])missingValue)[0];
                else if (missingValue is System.Single[])
                    missingValue = ((System.Single[])missingValue)[0];
                else if (missingValue is System.Double[])
                    missingValue = ((System.Double[])missingValue)[0];
            }

            int xLen = (InputHyperCube.Schema.HasDimension(i, HyperCube.Axis.X)) ? 
                InputHyperCube.GetAxisLength(HyperCube.Axis.X) : 1;
            int yLen = InputHyperCube.Schema.HasDimension(i, HyperCube.Axis.Y) ? 
                InputHyperCube.GetAxisLength(HyperCube.Axis.Y) : 1;
            int zLen = InputHyperCube.Schema.HasDimension(i, HyperCube.Axis.Z) ? 
                InputHyperCube.GetAxisLength(HyperCube.Axis.Z) : 1;
            int tLen = InputHyperCube.Schema.HasDimension(i, HyperCube.Axis.T) ? 
                InputHyperCube.GetAxisLength(HyperCube.Axis.T) : 1;

            // Create a new array to store the objects
            object[] objArray = new object[xLen * yLen * zLen * tLen];
            int resultArrayCounter = 0;
            for (int x = 0; x < xLen; x++)
            {
                for (int y = 0; y < yLen; y++)
                {
                    for (int z = 0; z < zLen; z++)
                    {
                        for (int t = 0; t < tLen; t++)
                        {
                            if (missingValue.ToString() != (InputHyperCube[x, y, z, t][i]).ToString())
                            {
                                objArray[resultArrayCounter] = InputHyperCube[x, y, z, t][i];
                                resultArrayCounter++;
                            }
                        }
                    }
                }
            }

            // Calcualte the median as of now it is the same for even and odd number
            // of elements
            System.Array.Resize(ref objArray, resultArrayCounter);
            System.Array.Sort(objArray);
            this.Median = (objArray[(objArray.Length / 2)]);

            return ActivityExecutionStatus.Closed;
        }

        #endregion

    }

    #endregion
}